/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Armin Lunkeit
 *
 * This implementation is part of math-core
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
//
//  fraction.cpp
//  math-core
//
//  Created by Armin Lunkeit on 15.02.14.
//  Copyright (c) 2014 Armin Lunkeit. All rights reserved.
//
#ifndef math_core_fraction_h
#include "fraction.h"
#endif

#include <iostream>
#include <cmath>

std::ostream& operator<<(std::ostream& lhs, Fraction const& rhs)
{
    if( rhs.isFraction())
        lhs << rhs._nominator << "/" << rhs._denominator;
    else
        lhs << rhs._nominator;
    return lhs;
}

Fraction::Fraction()
:   _nominator( 0 ),
    _denominator(1 )
{
    
}

Fraction::Fraction( int64_t n, int64_t d )
:   _nominator( n ),
    _denominator( d )
{
    normalize();
}

Fraction::Fraction( const char* number )
:   _nominator( 0 ),
    _denominator(1 )
{
    std::string str( number );
    
    size_t pos = str.find( "/" );
    
    if( pos != std::string::npos )
    {
        std::string s1 = str.substr( 0, pos );
        std::string s2 = str.substr( ++pos, str.size());
        
        std::cout << "s1: " << s1 << " s2 := " << s2 << std::endl;
        
        _nominator = atoll(s1.c_str());
        _denominator = atoll( s2.c_str());
    }
    else
    {
        _nominator = atoll( number );
        _denominator = 1;
    }
}


Fraction::Fraction( long double x )
: _nominator(0),
_denominator(1)
{
    
    initFromDouble( x );
}

Fraction::~Fraction()
{
    
}

int64_t Fraction::gcd( int64_t a, int64_t b )
{
    if( b == 0 )
        return a;
    else
        return gcd( b, a % b );
}

Fraction Fraction::operator + ( Fraction const& rhs  )
{
    return Fraction(
                    _nominator * rhs._denominator + rhs._nominator * _denominator,
                    _denominator * rhs._denominator);
}

Fraction Fraction::operator + ( uint32_t const& v )
{
    return Fraction( _nominator + v * _denominator, _denominator );
}

Fraction Fraction::operator += (Fraction const& rhs )
{
    *this = *this + rhs;
    return *this;
}

Fraction Fraction::operator += (uint32_t const& rhs )
{
    *this = *this + rhs;
    return *this;
}

Fraction Fraction::operator-( Fraction const& rhs )
{
    return Fraction(
                    _nominator * rhs._denominator - rhs._nominator * _denominator,
                    _denominator * rhs._denominator);

}

Fraction Fraction::operator-( uint32_t const& v )
{
    return Fraction(
                    _nominator - v * _denominator,
                    _denominator );
}

Fraction Fraction::operator -= (Fraction const& v )
{
    *this = *this - v;
    return *this;
}

Fraction Fraction::operator -= (uint32_t const& v )
{
    *this = *this - v;
    return *this;
}

Fraction Fraction::operator / (Fraction const& rhs )
{
    return Fraction( _nominator * rhs._denominator, _denominator * rhs._nominator );
}

Fraction Fraction::operator / (uint32_t const& v )
{
    return Fraction( _nominator, _denominator * v );
}

Fraction Fraction::operator /= (Fraction const& v )
{
    *this = *this / v;
    return *this;
}

Fraction Fraction::operator /= (uint32_t const& v)
{
    *this = *this / v;
    return *this;
}

Fraction Fraction::operator * (Fraction const & rhs )
{
    return Fraction( _nominator * rhs._nominator, _denominator * rhs._denominator );
}

Fraction Fraction::operator * (uint32_t const v )
{
    return Fraction( _nominator * v, _denominator * v );
}

Fraction Fraction::operator *= (uint32_t const v)
{
    *this = Fraction( _nominator *v, _denominator );
    return *this;
}

Fraction Fraction::operator *= (Fraction const & v )
{
    *this = Fraction( _nominator * v._nominator, _denominator * v._denominator );
    return *this;
}

void Fraction::normalize()
{
    int64_t cd = gcd( _nominator, _denominator );
    _nominator /= cd;
    _denominator /= cd;
    
    if( _nominator >= 0 && _denominator < 0 )
    {
        _nominator *= -1;
        _denominator *= -1;
    }
}

void Fraction::initFromDouble( long double x )
{
    std::cout << "value of x := " << x << std::endl;
    
    double epsilon = 1.E-12;
    
    typedef std::pair< int64_t, int64_t > F; // nominator; denominator
    
    const int sign = x < 0.0 ? -1: 1;
    
    if( sign < 0 )
        x = -x;
    
    // store the
    const int fp = int( x );        // fp = fractional part
    
    x -= fp;                        // store fractional part
    
    // In case the the fractional part is smaller than the
    // precision return here. The value is treated as Integer
    if( x <= epsilon )
    {
        _nominator = sign * fp;
        _denominator = 1;
        return;
    }
    
    // Set up an Farey sequence to determine the fractional
    // representation of the double value given.
    for( F q1( 0, 1 ), q2( 1, 1 );;)
    {
        const F med( q1.first + q2.first, q1.second + q2.second );
        
        const double r = double( med.first ) / med.second;
        
        if( std::abs( r - x ) < epsilon )
        {
            _nominator = sign * (med.first + fp * med.second);
            _denominator = med.second;
            return;
        }
        
        if( x < r )
            q2 = med;
        else
            q1 = med;
    }

}


